/*
*    Qizx/open 3.0 beta
*
* This code is the open-source version of Qizx.
* Copyright (C) 2004-2009 Axyana Software -- All rights reserved.
*
* The contents of this file are subject to the Mozilla Public License 
*  Version 1.1 (the "License"); you may not use this file except in 
*  compliance with the License. You may obtain a copy of the License at
*  http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
*  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
*  for the specific language governing rights and limitations under the
*  License.
*
* The Initial Developer of the Original Code is Xavier Franc - Axyana Software.
*
*/
using System;
using Scorer = System.Qizx.Api.FullText.Scorer;
namespace System.Qizx.Queries.Iterators
{
	
	/// <summary> Union of iterators.
	/// In enumeration mode, simply find first matching iterator, without sort: 
	/// not quite fast, but much more memory efficient.
	/// In predicate mode, no need to check order, so test each iterator in turn.
	/// </summary>
	public class OrIterator:PolyIterator
	{
		public override int NodeSpan
		{
			get
			{
				return matchEnd - curNodeId;
			}
			
		}
		public override int Depth
		{
			get
			{
				return current == null?0:current.Depth;
			}
			
		}
		protected internal int iterCount;
		protected internal PostingIterator current;
		protected internal int activeCnt;
		// iterators having a hit in current document:
		protected internal PostingIterator[] active;
		
		public OrIterator(PostingIterator[] iterators):this(iterators, iterators.Length)
		{
		}
		
		public OrIterator(PostingIterator[] iterators, int count):base(iterators)
		{
			iterCount = count;
			active = new PostingIterator[count];
		}
		
		public override PostingIterator bornAgain()
		{
			return copyConstraints(new OrIterator(bornAgain(iterators, iterCount)));
		}
		
		public override bool skipToDoc(int docId)
		{
			curNodeId = - 1;
			activeCnt = 0;
			// take the min doc of iterators: 
			// create list of active iterators which are on same current doc
			curDocId = System.Qizx.Queries.Iterators.PostingIterator_Fields.MAX_DOCID;
			for (int i = iterCount; --i >= 0; )
			{
				PostingIterator it = iterators[i];
				if (!it.skipToDoc(docId))
					continue;
				int doc = it.DocId;
				if (doc < curDocId)
				{
					activeCnt = 1;
					active[0] = it;
					curDocId = doc;
					curNodeId = - 1;
				}
				else if (doc == curDocId)
				{
					active[activeCnt++] = it;
				}
			}
			
			return activeCnt > 0;
		}
		
		protected internal override bool basicSkipToNode(int nodeId, int limit)
		{
			curNodeId = System.Qizx.Queries.Iterators.PostingIterator_Fields.MAX_NODEID;
			current = null;
			matchEnd = nodeId;
			// consider only active iterators: take first (ordered)
			for (int i = activeCnt; --i >= 0; )
			{
				PostingIterator it = active[i];
				if (it != null)
				{
					if (!it.skipToNode(nodeId, limit))
					{
						if (it.reachedEnd())
							active[i] = null;
					}
					else
					{
						int node = it.NodeId;
						if (node < curNodeId)
						{
							curNodeId = node;
							current = it;
							matchEnd = it.NodeEnd;
						}
					}
				}
			}
			// -System.err.println("curNodeId "+curNodeId+" "+current);
			return current != null;
		}
		
		public override bool inRange(int rangeStart, int rangeEnd)
		{
			for (int i = 0; i < activeCnt; i++)
			{
				PostingIterator it = active[i];
				if (it != null && it.inRange(rangeStart, rangeEnd))
				{
					curNodeId = it.NodeId;
					matchEnd = it.NodeEnd;
					
					if ((constraints == null || checkFTConstraints(rangeStart, rangeEnd)) && checkFilters())
					{
						current = it;
						return true;
					}
				}
			}
			return false;
		}
		
		public override void  resetToNode(int nodeId)
		{
			// some iterators may have been desactivated on eod, so check again
			activeCnt = 0;
			for (int i = iterCount; --i >= 0; )
			{
				PostingIterator it = iterators[i];
				if (it.DocId == curDocId)
				{
					active[activeCnt++] = it;
					it.resetToNode(nodeId);
				}
			}
			currentResetPos = nodeId;
			curNodeId = nodeId - 1;
		}
		
		public override float computeWeighting(Scorer scorer)
		{
			float[] subWeights = new float[iterCount];
			for (int i = 0; i < iterCount; i++)
			{
				subWeights[i] = iterators[i].computeWeighting(scorer);
			}
			weightNorm = scorer.normOr(subWeights);
			return weight / weightNorm;
		}
		
		public override float computeScore(Scorer scorer)
		{
			if (subScores == null)
				subScores = new float[iterCount];
			for (int i = 0; i < iterCount; i++)
			{
				if (iterators[i].DocId == DocId)
					subScores[i] = iterators[i].computeScore(scorer);
				else
					subScores[i] = 0;
			}
			return weight * weightNorm * scorer.scoreOr(subScores, iterCount);
		}
	}
}